package com.wuxiaolong.framework;

import com.wuxiaolong.framework.annotation.*;
import com.wuxiaolong.framework.model.BeanDefinition;
import com.wuxiaolong.framework.service.BeanNameAware;
import com.wuxiaolong.framework.service.BeanPostProcessor;
import com.wuxiaolong.framework.service.InitializingBean;

import javax.annotation.Resource;
import java.io.File;
import java.lang.reflect.Field;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * Description: spring启动类
 *
 * @author 诸葛小猿
 * @date 2020-11-19
 */
public class MyApplicationContext {

    /**
     * 配置类，决定扫码的路径
     */
    private Class configClass;

    /**
     * 扫码时，将每个具有扫描注解的class都解析成一个对象BeanDefinition
     * 并存到map中
     * @param configClass
     */
    private Map<String,BeanDefinition> beanDefinitionMap = new HashMap<String,BeanDefinition>();

    /**
     * 单例池
     */
    private Map<String,Object> singletonObjects = new HashMap<String,Object>();

    /**
     * 后置处理器的统一管理  扫描是设置值，创建对象时调用
     * @param configClass
     */
    private List<BeanPostProcessor> beanPostProcessorList = new ArrayList<BeanPostProcessor>();

    public MyApplicationContext(Class configClass) {
        this.configClass = configClass;

        // 启动时主要完成两步：
        // 1.扫码 获得beanDefinition对象
        scan();

        // 2.创建bean(非懒加载的单例bean)
        createNonLazyBean();

    }

    private void createNonLazyBean(){
        for(String beanName : beanDefinitionMap.keySet()){
            BeanDefinition beanDefinition = beanDefinitionMap.get(beanName);
            if(beanDefinition.getScope().equals("singleton") && ! beanDefinition.getLazy()){
                // 创建bean
                Object o = createBean(beanDefinition,beanName);
                singletonObjects.put(beanName,o);
            }
        }
    }

    private Object createBean( BeanDefinition beanDefinition,String beanName){
        Class beanClazz = beanDefinition.getBeanClazz();
        try{
            // 使用最简单的无参构造，实际spring框架会推断构造方法
            Object instance = beanClazz.getDeclaredConstructor().newInstance();

            // 注入字段的值 先byType 再byName     AutowiredAnnotationBeanPostProcessor
            for(Field field : beanClazz.getDeclaredFields()){
                if(field.isAnnotationPresent(Autowired.class)){

                    // 根据字段名字去单例池获取bean
                    Object bean = getBean(field.getName());
                    field.setAccessible(true);
                    field.set(instance,bean);
                }
            }

            for(BeanPostProcessor beanPostProcessor : beanPostProcessorList){
                beanPostProcessor.postMethod();
            }

//            // @Resource注解是byName的  CommonAnnotationBeanPostProcessor
//            for(Field field : beanClazz.getDeclaredFields()){
//                if(field.isAnnotationPresent(Resource.class)){
//
//                    // 根据字段名字去单例池获取bean
//                    Object bean = getBean(field.getName());
//                    field.setAccessible(true);
//                    field.set(instance,bean);
//                }
//            }
//            // MyAnnotationBeanPostProcessor
//            for(Field field : beanClazz.getDeclaredFields()){
//                if(field.isAnnotationPresent(Autowired.class)){
//
//                    // 根据字段名字去单例池获取bean
//                    Object bean = getBean(field.getName());
//                    field.setAccessible(true);
//                    field.set(instance,bean);
//                }
//            }


            // 处理BeanNameAware接口
            if(instance instanceof BeanNameAware){
                ((BeanNameAware) instance).setBeanName(beanName);
            }

            // 最后做相应的验证
            if(instance instanceof InitializingBean){
                ((InitializingBean) instance).afterPropertiesSet();
            }

            // AOP相关的处理

            return instance;
        }catch (Exception e){
            e.printStackTrace();
        }

        return null;
    }

    private void scan(){

        if(configClass.isAnnotationPresent(ComponentScan.class)){
            // 获得扫码的包名
            ComponentScan componentScanAnn = (ComponentScan) configClass.getAnnotation(ComponentScan.class);
            String scanPackage = componentScanAnn.value(); //com.wuxiaolong.service
            String scanPath = scanPackage.replaceAll("\\.","/");

            // 找到使用的类加载器，并在相对路径下找到class文件
            ClassLoader classLoader = MyApplicationContext.class.getClassLoader();
            URL url = classLoader.getResource(scanPath);
            File file = new File(url.getFile()); // file是文件夹 递归 todo
            for (File f : file.listFiles()){

                String absolutePath = f.getAbsolutePath();
                System.out.println(absolutePath);

                // 找到扫码的文件
                if(absolutePath.endsWith(".class")){
                    // 判断class文件中是否有注解 com.wuxiaolong.service.OrderService
                    String clazzName = absolutePath.substring(absolutePath.indexOf("com"),absolutePath.indexOf(".class")).replace("\\",".");
                    try{
                        Class clazz = classLoader.loadClass(clazzName); // com.wuxiaolong.service.OrderService
                        System.out.println(clazz);


                        // 2.1判断类上是否有Component注解
                        if(clazz.isAnnotationPresent(Component.class)){



                            // 找到实现了后置处理器beanPostProcessor的对象 判断这个类是否是分派来的
                            if(BeanPostProcessor.class.isAssignableFrom(clazz)){
                                BeanPostProcessor o = (BeanPostProcessor) clazz.getDeclaredConstructor().newInstance();
                                beanPostProcessorList.add(o);
                            }



                            BeanDefinition beanDefinition = new BeanDefinition();
                            beanDefinition.setBeanClazz(clazz);

                            Component componentAnno = (Component) clazz.getAnnotation(Component.class);
                            // 获取bean的名字
                            String beanName = componentAnno.value();

                            // 2.1.1如果有Lazy注解
                            if(clazz.isAnnotationPresent(Lazy.class)){
                                beanDefinition.setLazy(true);
                            }else {
                                beanDefinition.setLazy(false);
                            }

                            // 2.1.2 如果有Scope注解
                            if(clazz.isAnnotationPresent(Scope.class)){
                                Scope scopeAnno = (Scope) clazz.getAnnotation(Scope.class);
                                beanDefinition.setScope(scopeAnno.value());
                            }else {
                                // 2.1.3 这是单例的bean
                                beanDefinition.setScope("singleton");
                            }

                            beanDefinitionMap.put(beanName,beanDefinition);
                        }
                    }catch (Exception e){
                        e.printStackTrace();
                    }
                }


            }


        }
    }

    /**
     *  通过bean的名字获得bean
     *  要知道bean是单例的还是原型的。
     *
     * @param beanName
     * @return
     */
    public Object getBean(String beanName ){
        //
        if(! beanDefinitionMap.containsKey(beanName)){
            throw new NullPointerException("bean name:"+beanName+" not exist! ");
        }

        BeanDefinition beanDefinition = beanDefinitionMap.get(beanName);
        Object o = null;
        if("singleton".equals(beanDefinition.getScope())){
            // 单例池
            o = singletonObjects.get(beanName);

        }else if("prototype".equals(beanDefinition.getScope())){
            // 创建一个bean 并放入单例池 todo
             o = createBean(beanDefinition,beanName);
            singletonObjects.put(beanName,o);
        }

        // 创建并放入单例池 todo
        if(o == null){
            createBean(beanDefinition,beanName);
            singletonObjects.put(beanName,o);
        }


        return o;
    }
}
